﻿/********
* 
* 
*  Description:
*  Create By kain.hong at 7/21/2010 3:27:31 PM
*  
*
*  Revision History:
*  Date                  Who                 What
*  
* 
*/
#region namespace

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using Cherry.Agile.Data.DataAccess;
using System.Data.Objects;
using System.Data.EntityClient;
using System.Data.Objects.DataClasses;
using System.Threading;
using System.Data;
using System.Data.Metadata.Edm;
using System.Linq.Expressions;
using Cherry.Agile.Data.Internal;
#endregion


namespace Cherry.Agile.Data.Repository
{
    /// <summary>
    /// 提供对Entity Framework的IRepository的实现<see cref="IRepository"/>
    /// </summary>
    /// <typeparam name="T">模型类</typeparam>
    public abstract class ObjectRepository : IRepository, IDisposable
    {
        private string containerName;
        /// <summary>
        /// 内部DBConnection <see cref="DbConnection"/>
        /// </summary>
        private DbConnection Connection
        {
            get;
            set;
        }

        private ObjectContextWraper _wraper;
        private ObjectContext _context;
        /// <summary>
        /// 当前操作的ObjectContext <see cref="ObjectContext"/>
        /// </summary>
        internal protected ObjectContext ObjectContext
        {
            get
            {

                if (_context == null)
                    Init();

                return _context;
            }
            private set { _context = value; }

        }

        /// <summary>
        /// T所在模型中的Container名称
        /// </summary>
        protected virtual string ContainerName
        {
            get { return containerName; }
        }

        /// <summary>
        /// T所对应的ObjectSet名称
        /// </summary>
        protected abstract string ObjectSetName
        {
            get;
        }

        /// <summary>
        /// 当前对象是否已经释放
        /// </summary>
        public bool IsDisposed { get; private set; }

        #region ctor
        /// <summary>
        /// 构造函数
        /// </summary>
        public ObjectRepository() :
            this(string.Empty)
        {

        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="containerName">T所在模型中的容器名称</param>
        public ObjectRepository(string containerName) :
            this(containerName, null)
        {

        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="containerName">T所在模型中的容器名称</param>
        /// <param name="connection">处理数据的EntityConnection <see cref="EntityConnection"/></param>
        public ObjectRepository(string containerName, DbConnection connection)
        {
            this.Connection = connection;
            this.containerName = containerName;
        }

        /// <summary>
        /// 初始化容器
        /// </summary>
        /// <param name="containerName">容器名称</param>
        protected virtual void Init()
        {
            if (this.Connection == null)
                Connection = DbConnectionScope.Current.GetOpenedConnection();

            _wraper = new ObjectContextWraper(Connection, ContainerName);
            ObjectContext = _wraper.Reference(ContainerName);
            _repository = this;
            System.Diagnostics.Trace.TraceInformation(string.Format("ObjectRepository\t{0} created", this.GetType().Name));
        }
        #endregion

        /// <summary>
        /// 根据传入的ESql获取一个查询
        /// </summary>
        /// <param name="query">标准的ESql语句</param>
        /// <returns>IQueryable <see cref="IQueryable"/></returns>
        /// <example>
        /// <code>
        ///  using (var scope = new DbConnectionScope())
        /// {
        ///     var script = @"SELECT VALUE c FROM  Customer AS c order by c.Country SKIP @skip LIMIT @limit";
        ///     var q = new CustomerRepository().CreateQuery(script);
        ///     q.Parameters.Add(new ObjectParameter("skip", 0));
        ///     q.Parameters.Add(new ObjectParameter("limit", 5));
        ///     var count = q.Count();
        ///     Assert.IsTrue(count == 5);
        /// }
        /// </code>
        /// </example>
        protected virtual IQueryable<T> CreateQuery<T>(string query) where T : class
        {
            return Repository.CreateQuery<T>(query);
        }

        /// <summary>
        /// 根据传入的ESql获取一个查询
        /// </summary>
        /// <param name="query">标准的ESql语句</param>
        /// <param name="params"><see cref="ObjectParameter"/></param>
        /// <returns>>ObjectQuery <see cref="ObjectQuery"/></returns>
        protected virtual ObjectQuery<T> CreateObjectQuery<T>(string query, params ObjectParameter[] @params) where T : class
        {
            return Repository.CreateQuery<T>(query, @params) as ObjectQuery<T>;
        }

        /// <summary>
        /// 创建<see cref="ObjectQuery"/>
        /// </summary>
        /// <example>
        /// <code>
        /// <![CDATA[
        /// public void Select()
        /// {
        ///     var customers = Repository.GetCustomers();
        ///     var q = from c in customers select c;
        ///     Assert.IsTrue(q.Count() == customers.Count);
        /// }
        ///         
        /// public void SelectWhere()
        /// {
        ///     var customers = Repository.GetCustomers();
        ///     var q = from c in customers where c.CustomerID == 1 select c;
        ///     Assert.IsTrue(q.Count() == 1);
        /// }
        /// 
        /// public void LikeCondition()
        /// {
        ///     var q = from c in Repository.GetCustomers()
        ///             where c.Name.Contains("小")
        ///             select c;
        /// 
        ///     Assert.IsTrue(q.Count() == 2);
        /// }
        /// 
        /// public void InCondition()
        /// {
        ///     var ids = new int[] { 1, 2, 3 };
        ///     var q = from c in Repository.GetCustomers()
        ///             where ids.Contains(c.CustomerID)
        ///             select c;
        ///     Assert.IsTrue(q.Count() == 3);
        /// }
        /// 
        /// public void OrderBy()
        /// {
        ///     var customers = Repository.GetCustomers();
        ///     var q = from c in customers
        ///             orderby c.CustomerID descending
        ///             select c;
        ///     var first = q.First();
        ///     Assert.IsTrue(first.CustomerID == customers[customers.Count - 1].CustomerID);
        /// }
        /// 
        /// 
        /// 
        /// public void Paging()
        /// {
        ///     var customers = Repository.GetCustomers();
        ///     var q = customers.Skip(1).Take(2).OrderBy(c => c.CustomerID);
        ///     var result = q.ToList();
        ///     Assert.IsTrue(result.Count == 2);
        ///     Assert.IsTrue(result[0].CustomerID == customers[1].CustomerID);
        /// }
        /// 
        /// public void PaginationOrderBy()
        /// {
        ///     var customers = Repository.GetCustomers();
        ///     var page = new Pagination();
        ///     page.PageSize = 5;
        ///     page.Paging = true;
        ///     var q = customers.Select(c => c).AsQueryable();
        ///     page.OrderBy<Customer>(p => p.CreateDate);
        ///     var experct = page.ParsQueryPaging<Customer>(q).ToList();
        ///     Assert.IsTrue(page.RowCount.HasValue);
        ///     Assert.IsTrue(experct.Count <= page.PageSize);
        /// }
        /// 
        /// 
        /// public void PagingOrderByDynamic()
        /// {
        ///     var customers = Repository.GetCustomers();
        ///     var page = new Pagination();
        ///     page.PageSize = 5;
        ///     page.Paging = true;
        ///     var q = customers.Select(c => c).AsQueryable();
        ///     page.OrderBy("CreateDate", true);
        ///     var experct = page.ParsQueryPaging<Customer>(q).ToList();
        ///     Assert.IsTrue(page.RowCount.HasValue);
        ///     Assert.IsTrue(experct.Count <= page.PageSize);
        /// }
        /// 
        /// 
        /// public void SelectGetDynamicObject()
        /// {
        ///     var customers = Repository.GetCustomers();
        ///     var q = from c in customers
        ///             select new { ID = c.CustomerID, Name = c.Name };
        ///     var item = q.First();
        ///     Assert.IsTrue(item.ID == customers[0].CustomerID);
        /// }
        /// 
        /// public void GroupBy()
        /// {
        ///     var products = Repository.GetProducts();
        ///     var q = from p in products
        ///             group p by p.Category into ps
        ///             where ps.Key == "日用品"
        ///             select new { Key = ps.Key, Count = ps.Count() };
        ///     var result = q.FirstOrDefault();
        ///     Assert.IsTrue(result.Count == 2 && result.Key == "日用品");
        /// }
        /// 
        /// public void Join()
        /// {
        ///     var q = from c in Repository.GetCustomers()
        ///             join o in Repository.GetOrders() on c.CustomerID equals o.CustomerID
        ///             select o;
        /// 
        ///     Assert.IsTrue(q.Count() == Repository.GetOrders().Count);
        /// }
        /// 
        /// public void Join2()
        /// {
        ///     var q = from c in Repository.GetCustomers()
        ///             from o in Repository.GetOrders()
        ///             where c.CustomerID == o.CustomerID
        ///             select o;
        /// 
        ///     Assert.IsTrue(q.Count() == Repository.GetOrders().Count);
        /// }
        /// 
        /// public void LeftJoin()
        /// {
        ///     var custTotalOrders = from c in Repository.GetCustomers()
        ///                           join o in Repository.GetOrders() on c.CustomerID equals o.CustomerID into co
        ///                           from o in co.DefaultIfEmpty()
        ///                           select new { c.Name, o.OrderDate };
        /// }
        /// ]]>
        /// </code>
        /// </example>
        /// <returns></returns>
        protected virtual ObjectQuery<T> CreateObjectQuery<T>() where T : class
        {
            return Repository.CreateQuery<T>() as ObjectQuery<T>;
        }

        /// <summary>
        /// 添加一个实例
        /// </summary>
        /// <param name="entity">待添加的实例</param>
        /// <returns>添加成功的实例</returns>
        /// <example><code>
        /// <![CDATA[
        /// using (var scope = new DbConnectionScope())
        /// {
        ///     var repository = new UserRepository();
        ///     var createDate = DateTime.Now;
        ///     var user = new Users() { Address = "New", CreateDate = createDate, Name = "John" };
        ///     user = repository.Add(user);
        ///     repository.SaveChanges();
        ///     Assert.IsTrue(user.Id > 0);
        ///
        /// }
        /// ]]>
        /// </code></example>
        public virtual T Add<T>(T entity) where T : class
        {
            return Repository.Add<T>(entity);
        }

        /// <summary>
        /// 删除一个实例
        /// </summary>
        /// <param name="entity">待删除实例</param>
        /// <example><code>
        /// <![CDATA[
        /// using (var scope = new DbConnectionScope())
        /// {
        ///     var repository = new UserRepository();
        ///     var actual = repository.GetBy(u => u.Id == userId);
        ///     Assert.IsNotNull(actual);
        ///     repository.Delete(actual);
        ///     repository.SaveChanges();
        ///     user = repository.GetBy(u => u.Id == userId);
        ///     Assert.IsNull(user);
        /// }
        /// ]]>
        /// </code></example>
        public virtual void Delete<T>(T entity) where T : class
        {
            Repository.Delete<T>(entity);
        }

        /// <summary>
        /// 根据条件删除，删除在调用之后就会更新到数据库中无需SaveChanges
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="where">条件表达式，只支持简单表达式</param>
        /// <example>
        /// <code><![CDATA[
        /// public void DeleteCustomerByIds(string[] ids)
        /// {
        ///    this.Delete<Customer>(c => ids.Contains(c.CustomerID));
        /// }
        /// ]]></code>
        /// </example>
        protected virtual int Delete<T>(Expression<Func<T, bool>> where) where T : class
        {
            return Repository.Delete<T>(where);
        }

        /// <summary>
        /// 批量更新,在调用之后就会更新到数据库中无需SaveChanges
        /// </summary>
        /// <typeparam name="T">更新实体类型</typeparam>
        /// <param name="set">更新表达式</param>
        /// <param name="where">条件表达式</param>
        /// <example>
        /// <code><![CDATA[
        /// public void SetCustomerLocation(string newCity, string[] ids)
        /// {
        ///    this.Update<Customer>(c => new Customer { City = newCity }, c => ids.Contains(c.CustomerID));
        /// }
        /// ]]></code>
        /// </example>
        protected virtual int Update<T>(Expression<Func<T, T>> set, Expression<Func<T, bool>> where) where T : class
        {
            return Repository.Update<T>(set, where);
        }

        /// <summary>
        /// 更新一个实例
        /// </summary>
        /// <param name="entity">待更新实例</param>
        /// <example><code>
        /// <![CDATA[
        /// using (var scope = new DbConnectionScope())
        /// {
        ///     var repository = new UserRepository();
        ///     var createDate = DateTime.Now;
        ///     var user = new Users() { Address = "New", CreateDate = createDate, Name = "John" };
        ///     user = repository.Add(user);
        ///     repository.SaveChanges();
        ///     Assert.IsTrue(user.Id > 0);
        ///     var userId = user.Id;
        ///     var actual = repository.GetBy(u => u.Id == userId);
        ///     Assert.IsNotNull(actual);
        ///     Assert.IsTrue(actual.CreateDate == createDate);
        ///     actual.Status = 1;
        ///     repository.Update(actual);
        ///     repository.SaveChanges();
        ///     user = repository.GetBy(u => u.Id == userId);
        ///     Assert.IsNotNull(user);
        ///     Assert.IsTrue(user.Status == 1);
        /// }
        /// ]]>
        /// </code></example>
        public virtual void Update<T>(T entity) where T : class
        {
            Repository.Update<T>(entity);
        }

        /// <summary>
        /// 提交所有修改
        /// </summary>
        public virtual int SaveChanges()
        {
            var result = ObjectContext.SaveChanges(SaveOptions.AcceptAllChangesAfterSave);
            return result;
        }

        /// <summary>
        /// 撤销所有的修改
        /// </summary>
        public virtual void DetectChanges()
        {
            ObjectContext.DetectChanges();
        }

        #region public methods


        /// <summary>
        /// 根据条件获取一个实例
        /// </summary>
        /// <param name="condition">查询条件</param>
        /// <returns>符合条件的实例</returns>
        /// <example><code>
        /// <![CDATA[
        /// using (var scope = new DbConnectionScope())
        /// {
        ///     var repository = new UserRepository();
        ///     var actual = repository.GetBy(u => u.Id == userId);
        ///     Assert.IsNotNull(actual);
        /// }
        /// ]]>
        /// </code></example>
        public virtual T GetBy<T>(Expression<Func<T, bool>> condition) where T : class
        {

            return Repository.CreateQuery<T>().FirstOrDefault(condition);
        }

        /// <summary>
        /// 提供数据源的查询进行计算的功能
        /// </summary>
        /// <returns>数据集的查询集</returns>
        public virtual IQueryable<T> CreateQuery<T>() where T : class
        {
            return Repository.CreateQuery<T>();
        }
        #endregion

        #region private methods
        #endregion


        /// <summary>
        /// 释放对象
        /// </summary>
        public virtual void Dispose()
        {
            IsDisposed = true;
            System.Diagnostics.Trace.TraceInformation(string.Format("ObjectRepository\t{0} disposed", this.GetType().Name));
            GC.SuppressFinalize(this);

        }

        IRepository _repository;
        private IRepository Repository
        {
            get
            {
                if (_repository == null)
                    Init();
                return _repository;
            }
        }

        TEntity IRepository.Add<TEntity>(TEntity entity)
        {
            var set = ObjectContext.CreateObjectSet<TEntity>();
            set.AddObject(entity);
            return entity;
        }

        TEntity IRepository.Update<TEntity>(TEntity entity)
        {
            var key = ObjectContext.CreateEntityKey(ObjectSetName, entity);
            object obj;
            if (ObjectContext.TryGetObjectByKey(key, out obj))
            {
                ObjectContext.ApplyCurrentValues<TEntity>(ObjectSetName, entity);
            }
            else
            {
                ObjectContext.AttachTo(ObjectSetName, entity);
            }
            return entity;
        }

        void IRepository.Delete<TEntity>(TEntity entity)
        {
            ObjectContext.DeleteObject(entity);
        }

        TEntity IRepository.GetBy<TEntity>(Expression<Func<TEntity, bool>> condition)
        {
            return ObjectContext.CreateObjectSet<TEntity>().Where(condition).FirstOrDefault();
        }

        IQueryable<TEntity> IRepository.CreateQuery<TEntity>()
        {
            return ObjectContext.CreateObjectSet<TEntity>();
        }

        IQueryable<TEntity> IRepository.CreateQuery<TEntity>(string query, params ObjectParameter[] @params)
        {
            return ObjectContext.CreateQuery<TEntity>(query, @params);
        }

        ObjectResult<T> IRepository.ExecuteFunction<T>(string funName, params ObjectParameter[] @params)
        {
            var temp = funName.Split('.');
            if (temp.Length != 2)
                funName = ContainerName + "." + funName;

            return ObjectContext.ExecuteFunction<T>(funName, @params);
        }

        int IRepository.ExecuteFunction(string funName, params ObjectParameter[] @params)
        {
            return ObjectContext.ExecuteFunction(funName, @params);
        }

        ObjectResult<T> IRepository.ExecuteStoreQuery<T>(string script, params ObjectParameter[] @params)
        {
            var cmd = (ObjectContext.Connection as EntityConnection).StoreConnection.CreateCommand();
            var parameters = @params.Select(c =>
                {
                    var p = cmd.CreateParameter();
                    p.ParameterName = c.Name;
                    p.Value = c.Value;
                    return p;
                });
            return ObjectContext.ExecuteStoreQuery<T>(script, parameters);
        }

        int IRepository.Delete<T>(Expression<Func<T, bool>> where)
        {
            return ObjectContext.DeleteAll<T>(where);
        }

        int IRepository.Update<T>(Expression<Func<T, T>> set, Expression<Func<T, bool>> where)
        {
            return ObjectContext.UpdateAll<T>(set, where);
        }

    }

    /// <summary>
    /// ObjectRepository 泛型实现
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    public abstract class ObjectRepository<T> : ObjectRepository
                                  where T : class,new()
    {
        /// <summary>
        /// ObjectSet Name
        /// </summary>
        protected override string ObjectSetName
        {
            get { return typeof(T).Name; }
        }

        /// <summary>
        /// 获取对应的Entities
        /// </summary>
        public IQueryable<T> Entities
        {
            get { return this.CreateQuery<T>(); }
        }

        /// <summary>
        /// 添加一个实体
        /// </summary>
        /// <param name="entity">实体实例</param>
        /// <returns>添加成功的对象实例</returns>
        public virtual T Add(T entity)
        {
            return base.Add<T>(entity);
        }

        /// <summary>
        /// 更新一个实例
        /// </summary>
        /// <param name="entity">实体实例</param>
        /// <returns>保存成功的对象实例</returns>
        public virtual T Update(T entity)
        {
            base.Update<T>(entity);
            return entity;
        }

        /// <summary>
        /// 删除一个实例
        /// </summary>
        /// <param name="entity">实体实例</param>
        public virtual void Delete(T entity)
        {
            base.Delete<T>(entity);
        }

        /// <summary>
        /// 根据条件删除实例
        /// </summary>
        /// <param name="where">条件</param>
        protected virtual void Delete(Expression<Func<T, bool>> where)
        {
            base.Delete<T>(where);
        }

        /// <summary>
        /// 根据条件更新实例
        /// </summary>
        /// <param name="set">更新字段</param>
        /// <param name="where">条件</param>
        protected virtual void Update(Expression<Func<T, T>> set, Expression<Func<T, bool>> where)
        {
            base.Update<T>(set, where);
        }

        /// <summary>
        /// 获取一个简单实例
        /// </summary>
        /// <param name="where">条件</param>
        /// <returns>实例</returns>
        public T Get(Expression<Func<T, bool>> where)
        {
            return Entities.SingleOrDefault(where);
        }


    }
}
